package repo

import (
	"laundry-service/app/client/internal/types"
	"laundry-service/entity"

	"gorm.io/gorm"
)

type IUserDao interface {
	ListOrder(req *types.OrderlistReq) ([]*entity.Order, int64, error)
	GetProductItem(orderId int) ([]*entity.Product, error)
	GetUserById(userId int) (*entity.User, error)
	GetUserAuthByUserId(userId int) (*entity.UserAuth, error)
	GetUserAuthByAuthId(authId string) (*entity.UserAuth, error)
	GetOrderById(orderId int) (*entity.Order, error)
	GetOrderByExpOrderCode(code string) (*entity.Order, error)
	ClearAddrPreDefault(userId int64) error
	SaveAddr(a *entity.Address) error
	FindAddrById(userId int64, addrId uint) (*entity.Address, error)
	DeleteAddr(addr *entity.Address) error
	AddrList(userId int64) ([]*entity.Address, int64, error)
	SetDefaultAddr(addr *entity.Address) error
	OrderRefundSave(bo *entity.OrderRefund) error
	GetOrderRefundByNo(no string) (*entity.OrderRefund, error)
	TxRefund(o *entity.Order, refund *entity.OrderRefund, log *entity.OrderLog) error
	GetAdminById(userId int) (*entity.Admin, error)

	SaveReservationOrder(order *entity.ReservationOrder) error
	FindReservationOrderByOrderId(orderId int) (*entity.ReservationOrder, error)
	FindOrderById(orderId int) (*entity.Order, error)
	GetOrderByNumber(orderNumber string) (*entity.Order, error)
	FindCategoryByIds(ids []int) ([]*entity.Category, error)
	TxSaveOrderState(u *entity.Order, bp *entity.OrderPayment, ol *entity.OrderLog) error
	TxWXRefundNotify(order *entity.Order, orderRefund *entity.OrderRefund, ol *entity.OrderLog) error

	SavePaymentLog(log *entity.OrderLog) error
	SaveOrder(o *entity.Order) error
	SaveOrderLog(log *entity.OrderLog) error
	SaveExpressLog(log *entity.ExpressLog) error
	TxExpressSignNtfCallback(order *entity.Order, log *entity.OrderLog) error
	SaveBagLog(log *entity.BagLog) error
	GetBagLogListByOrderId(orderId int) ([]*entity.BagLog, error)
	GetOrderLogByOrderId(orderId uint) ([]*entity.OrderLog, error)
	GetExpressLogByOrderId(orderId uint) ([]*entity.ExpressLog, error)
	SaveExpressBind(expressBind *entity.ExpressBind) error
	GetExpressBindByCustomNum(customNum string) (*entity.ExpressBind, error)

	GetOrderBySFBillCode(code string) (*entity.Order, error)
	GetExpressBindBySFBillCode(code string) (*entity.ExpressBind, error)

	UserSave(user *entity.User) error
	UserAuthSave(userAuth *entity.UserAuth) error
	UserInviteSave(userInvite *entity.UserInvite) error

	CouponSave(coupon *entity.Coupon) error
	CouponDelete(couponId int) error
	GetCouponById(couponId int) (*entity.Coupon, error)
	UserCouponSave(userCoupon *entity.UserCoupon) error
	GetOrderItemByOrderIdAndProductId(orderId int, productId int) (*entity.OrderItem, error)
	GetOrderItem(orderId int) ([]*entity.Product, error)

	UpdateUserCouponState(userCouponId int, state int) error
	GetCategoryList() ([]*entity.BagCategory, error)
	ListProduct(req *types.ProductlistReq) ([]*entity.Product, int64, error)

	SavePageShare(pageShare *entity.PageShare) error
	GetPageShareByPath(path string) (*entity.PageShare, error)
	GetPageShareById(id uint) (*entity.PageShare, error)
	GetPageShareList() ([]*entity.PageShare, error)
	DeletePageShare(id uint) error
	UpdatePageShare(pageShare *entity.PageShare) error
	GetUserByMobile(mobile string) (*entity.User, error)
	GetUserCoupon(couponId int, userId int) (*entity.UserCoupon, error)
	SaveUserCoupon(userCoupon *entity.UserCoupon) error
	GetUserInviteByUserId(userId int) (*entity.UserInvite, error)
	GetUserCouponByInvitee(couponId int, userId int, invitee int) (*entity.UserCoupon, error)

	GetProductById(productId int) (*entity.Product, error)
	GetProductsByIds(productIds []int) ([]*entity.Product, error)
	GetProductWithImages(productId int) (*entity.Product, error)
}

type UserDao struct {
	db *gorm.DB
}

func NewUserDao(db *gorm.DB) *UserDao {
	return &UserDao{db: db}
}

func (c *UserDao) GetAdminById(userId int) (*entity.Admin, error) {
	auth := new(entity.Admin)
	err := c.db.Where("id = ? ", userId).First(&auth).Error
	if err != nil {
		return nil, err
	}
	return auth, nil
}

func (c *UserDao) GetUserByMobile(mobile string) (*entity.User, error) {
	auth := new(entity.User)
	err := c.db.Where("mobile = ? ", mobile).First(&auth).Error
	if err != nil {
		return nil, err
	}
	return auth, nil
}

func (c *UserDao) UpdatePageShare(pageShare *entity.PageShare) error {
	return c.db.Model(&pageShare).Updates(pageShare).Error
}

func (c *UserDao) GetUserInviteByUserId(userId int) (*entity.UserInvite, error) {
	auth := new(entity.UserInvite)
	err := c.db.Where("user_id = ?  ", userId).First(&auth).Error
	if err != nil {
		return nil, err
	}
	return auth, nil
}

func (c *UserDao) GetUserCouponByInvitee(couponId int, userId int, invitedId int) (*entity.UserCoupon, error) {
	order := new(entity.UserCoupon)
	err := c.db.Where("coupon_id = ? and user_id = ? and invitee = ?", couponId, userId, invitedId).First(&order).Error
	return order, err
}

func (c *UserDao) GetUserCoupon(couponId int, userId int) (*entity.UserCoupon, error) {
	order := new(entity.UserCoupon)
	err := c.db.Where("coupon_id = ? and user_id = ?", couponId, userId).First(&order).Error
	return order, err
}

func (c *UserDao) SaveUserCoupon(userCoupon *entity.UserCoupon) error {
	return c.db.Save(&userCoupon).Error
}

func (c *UserDao) SavePageShare(pageShare *entity.PageShare) error {
	return c.db.Save(pageShare).Error
}

func (c *UserDao) GetPageShareList() ([]*entity.PageShare, error) {
	var pageShareList []*entity.PageShare
	err := c.db.Find(&pageShareList).Error
	if err != nil {
		return nil, err
	}
	return pageShareList, nil
}

func (c *UserDao) GetPageShareByPath(path string) (*entity.PageShare, error) {
	pageShare := new(entity.PageShare)
	err := c.db.Where("path = ?", path).First(&pageShare).Error
	if err != nil {
		return nil, err
	}
	return pageShare, err
}

func (c *UserDao) GetPageShareById(id uint) (*entity.PageShare, error) {
	pageShare := new(entity.PageShare)
	err := c.db.Where("id = ?", id).First(&pageShare).Error
	if err != nil {
		return nil, err
	}
	return pageShare, err
}

func (c *UserDao) DeletePageShare(id uint) error {
	return c.db.Where("id = ?", id).Delete(&entity.PageShare{}).Error
}

func (c *UserDao) GetOrderItem(orderId int) ([]*entity.Product, error) {
	var p []*entity.Product
	err := c.db.Table("l_order_item oi").Select("p.*").
		Joins("left join l_product p on oi.product_id = p.id").
		Where("oi.order_id = ?", orderId).Find(&p).Error
	return p, err
}

func (c *UserDao) ListProduct(req *types.ProductlistReq) ([]*entity.Product, int64, error) {

	var s []*entity.Product
	page := req.Page
	pageSize := req.PageSize
	if page == 0 {
		page = 1
	}
	if pageSize == 0 {
		pageSize = 1000
	}
	db := c.db.Table("l_product").Where("state = 1")

	if req.CategoryId > 0 {
		db = db.Where(" bag_category_id = ?", req.CategoryId)
	}

	var count int64
	err := db.Count(&count).Error
	if err != nil {
		return nil, 0, err
	}
	if page > 0 && pageSize > 0 {
		db = db.Limit(pageSize).Offset((page - 1) * pageSize)
	}
	err = db.Order("sort desc").Find(&s).Error
	return s, count, err
}

func (c *UserDao) GetOrderItemByOrderIdAndProductId(orderId int, productId int) (*entity.OrderItem, error) {
	orderItem := new(entity.OrderItem)
	err := c.db.Where("order_id = ? And product_id = ?", orderId, productId).First(&orderItem).Error
	if err != nil {
		return nil, err
	}
	return orderItem, err
}

func (c *UserDao) CouponSave(coupon *entity.Coupon) error {
	return c.db.Save(coupon).Error
}

func (c *UserDao) CouponDelete(couponId int) error {
	return c.db.Model(&entity.Coupon{}).Where("id = ?", couponId).Update("state", -1).Error
}

func (c *UserDao) UserCouponSave(userCoupon *entity.UserCoupon) error {
	return c.db.Save(userCoupon).Error
}

func (c *UserDao) GetCouponById(couponId int) (*entity.Coupon, error) {
	coupon := new(entity.Coupon)
	err := c.db.Where("id = ?", couponId).First(&coupon).Error
	if err != nil {
		return nil, err
	}
	return coupon, err
}

func (c *UserDao) SaveExpressBind(expressBind *entity.ExpressBind) error {
	return c.db.Save(expressBind).Error
}

func (c *UserDao) GetOrderBySFBillCode(code string) (*entity.Order, error) {
	order := new(entity.Order)
	err := c.db.Where("expressBillCode = ?", code).First(&order).Error
	if err != nil {
		return nil, err
	}
	return order, err
}

func (c *UserDao) GetExpressBindBySFBillCode(code string) (*entity.ExpressBind, error) {
	expressBind := new(entity.ExpressBind)
	err := c.db.Where("first_waybill_no = ? or second_waybill_no = ?", code, code).First(&expressBind).Error
	if err != nil {
		return nil, err
	}
	return expressBind, err
}

func (c *UserDao) GetExpressBindByCustomNum(customNum string) (*entity.ExpressBind, error) {
	expressBind := new(entity.ExpressBind)
	err := c.db.Where("first_custom_order_code = ? or second_custom_order_code = ?", customNum, customNum).First(&expressBind).Error
	if err != nil {
		return nil, err
	}
	return expressBind, err
}

func (c *UserDao) GetOrderByExpOrderCode(code string) (*entity.Order, error) {
	o := new(entity.Order)
	err := c.db.Model(&entity.Order{}).Where("expressOrderCode = ?", code).First(&o).Error
	if err != nil {
		return nil, err
	}
	return o, err
}

func (c *UserDao) GetCategoryList() ([]*entity.BagCategory, error) {
	var category []*entity.BagCategory
	err := c.db.Find(&category).Error
	if err != nil {
		return nil, err
	}
	return category, nil
}

func (c *UserDao) ListOrder(req *types.OrderlistReq) ([]*entity.Order, int64, error) {
	var s []*entity.Order
	page := req.Page
	pageSize := req.PageSize

	if page == 0 {
		page = 1
	}
	if pageSize == 0 {
		pageSize = 10
	}

	db := c.db.Table("l_order")
	if req.State != "all" {
		if req.State == "1" {
			db = db.Where(" state in (?,?,?)", 1, 3, 4)
		} else {
			db = db.Where(" state = ?", req.State)
		}
	}

	if req.UserId > 0 {
		db = db.Where(" user_id = ?", req.UserId)
	}

	var count int64
	err := db.Count(&count).Error
	if page > 0 && pageSize > 0 {
		db = db.Limit(pageSize).Offset((page - 1) * pageSize)
	}
	err = db.Order("created_at desc").Find(&s).Error
	if err != nil {
		return nil, 0, err
	}
	return s, count, err
}

func (c *UserDao) UpdateUserCouponState(userCouponId int, state int) error {
	return c.db.Model(&entity.UserCoupon{}).Where("id = ?", userCouponId).Update("state", state).Error
}

func (c *UserDao) GetProductItem(orderId int) ([]*entity.Product, error) {
	var p []*entity.Product
	err := c.db.Table("l_order_item oi").Select("p.*").
		Joins("left join l_product p on oi.product_id = p.id").
		Where("oi.order_id = ?", orderId).Find(&p).Error
	if err != nil {
		return nil, err
	}
	return p, err
}

func (c *UserDao) GetUserById(userId int) (*entity.User, error) {
	auth := new(entity.User)
	err := c.db.Where("id = ? ", userId).First(&auth).Error
	if err != nil {
		return nil, err
	}
	return auth, nil
}

func (c *UserDao) GetUserAuthByUserId(userId int) (*entity.UserAuth, error) {
	auth := new(entity.UserAuth)
	err := c.db.Where("user_id = ? ", userId).First(&auth).Error
	if err != nil {
		return nil, err
	}
	return auth, nil
}
func (c *UserDao) GetUserAuthByAuthId(authId string) (*entity.UserAuth, error) {
	auth := new(entity.UserAuth)
	err := c.db.Where("auth_id = ? ", authId).First(auth).Error
	if err != nil {
		return nil, err
	}
	return auth, err
}

func (c *UserDao) GetBagLogListByOrderId(orderId int) ([]*entity.BagLog, error) {
	var bagLogs []*entity.BagLog
	err := c.db.Where("order_id = ?", orderId).Find(&bagLogs).Error
	if err != nil {
		return nil, err
	}
	return bagLogs, nil
}

func (c *UserDao) GetOrderById(orderId int) (*entity.Order, error) {
	order := new(entity.Order)
	err := c.db.Where("id = ?", orderId).First(&order).Error
	if err != nil {
		return nil, err
	}
	return order, err
}

func (c *UserDao) ClearAddrPreDefault(userId int64) error {
	err := c.db.Model(&entity.Address{}).Where("user_id = ? And `default` = ?", userId, 1).Update("default", false).Error
	return err
}

func (c *UserDao) SaveAddr(a *entity.Address) error {
	return c.db.Create(a).Error
}

func (c *UserDao) FindAddrById(userId int64, addrId uint) (*entity.Address, error) {
	addr := new(entity.Address)
	err := c.db.Unscoped().Where("id = ? AND user_id = ?", addrId, userId).First(addr).Error
	if err != nil {
		return nil, err
	}
	return addr, nil
}

func (c *UserDao) DeleteAddr(addr *entity.Address) error {
	return c.db.Where("id = ?", addr.ID).Delete(&addr).Error
}
func (c *UserDao) SavePaymentLog(log *entity.OrderLog) error { return c.db.Save(log).Error }
func (c *UserDao) SetDefaultAddr(addr *entity.Address) error {
	return c.db.Model(&addr).Update("default", true).Error
}
func (c *UserDao) SaveOrderLog(log *entity.OrderLog) error      { return c.db.Save(log).Error }
func (c *UserDao) OrderRefundSave(bo *entity.OrderRefund) error { return c.db.Save(&bo).Error }
func (c *UserDao) SaveOrder(o *entity.Order) error              { return c.db.Save(o).Error }
func (c *UserDao) SaveExpressLog(log *entity.ExpressLog) error  { return c.db.Save(log).Error }
func (c *UserDao) SaveBagLog(log *entity.BagLog) error          { return c.db.Save(&log).Error }
func (c *UserDao) UserSave(user *entity.User) error {
	return c.db.Save(&user).Error
}
func (c *UserDao) UserAuthSave(userAuth *entity.UserAuth) error {
	return c.db.Save(&userAuth).Error
}

func (c *UserDao) UserInviteSave(userInvite *entity.UserInvite) error {
	return c.db.Save(&userInvite).Error
}

func (c *UserDao) GetOrderRefundByNo(no string) (*entity.OrderRefund, error) {
	or := new(entity.OrderRefund)
	err := c.db.Where("order_no = ?", no).First(&or).Error
	if err != nil {
		return nil, err
	}
	return or, err
}

func (c *UserDao) AddrList(userId int64) (addresses []*entity.Address, len int64, err error) {
	result := c.db.Where("user_id = ?", userId).Find(&addresses)
	if result.Error != nil {
		return nil, 0, err
	}
	return addresses, result.RowsAffected, nil
}

func (c *UserDao) TxRefund(o *entity.Order, refund *entity.OrderRefund, log *entity.OrderLog) error {
	if err := c.db.Save(&o).Error; err != nil {
		return err
	}
	if err := c.db.Save(&refund).Error; err != nil {
		return err
	}
	if err := c.db.Save(&log).Error; err != nil {
		return err
	}
	return nil
}

func (c *UserDao) TxExpressSignNtfCallback(order *entity.Order, log *entity.OrderLog) error {
	tx := c.db.Begin()
	if err := tx.Save(&order).Error; err != nil {
		tx.Rollback()
		return err
	}
	if err := tx.Save(&log).Error; err != nil {
		tx.Rollback()
		return err
	}
	tx.Commit()
	return nil
}

func (c *UserDao) SaveReservationOrder(order *entity.ReservationOrder) error {
	return c.db.Save(order).Error
}

func (c *UserDao) FindOrderById(orderId int) (*entity.Order, error) {
	order := new(entity.Order)
	err := c.db.Where("id = ?", orderId).First(&order).Error
	if err != nil {
		return nil, err
	}
	return order, err
}

func (c *UserDao) GetOrderByNumber(orderNumber string) (*entity.Order, error) {
	order := new(entity.Order)
	err := c.db.Where("number = ?", orderNumber).First(&order).Error
	if err != nil {
		return nil, err
	}
	return order, err
}

func (c *UserDao) FindReservationOrderByOrderId(orderId int) (*entity.ReservationOrder, error) {
	order := new(entity.ReservationOrder)
	err := c.db.Where("order_id = ?", orderId).First(&order).Error
	if err != nil {
		return nil, err
	}
	return order, err
}

func (c *UserDao) FindCategoryByIds(ids []int) ([]*entity.Category, error) {
	var category []*entity.Category
	err := c.db.Where("id in (?)", ids).Find(&category).Error
	if err != nil {
		return nil, err
	}
	return category, err
}

func (c *UserDao) TxSaveOrderState(u *entity.Order, bp *entity.OrderPayment, ol *entity.OrderLog) error {
	tx := c.db.Begin()

	if err := tx.Save(&u).Error; err != nil {
		tx.Rollback()
		return err
	}
	if bp != nil {
		if err := tx.Save(&bp).Error; err != nil {
			tx.Rollback()
			return err
		}
	}
	if err := tx.Save(&ol).Error; err != nil {
		tx.Rollback()
		return err
	}
	tx.Commit()
	return nil
}

func (c *UserDao) TxWXRefundNotify(order *entity.Order, orderRefund *entity.OrderRefund, ol *entity.OrderLog) error {
	tx := c.db.Begin()

	if err := tx.Save(&order).Error; err != nil {
		tx.Rollback()
		return err
	}
	if err := tx.Save(&orderRefund).Error; err != nil {
		tx.Rollback()
		return err
	}
	if err := tx.Save(&ol).Error; err != nil {
		tx.Rollback()
		return err
	}
	tx.Commit()
	return nil
}

func (c *UserDao) GetOrderLogByOrderId(orderId uint) ([]*entity.OrderLog, error) {
	var orderLogs []*entity.OrderLog
	err := c.db.Where("order_id = ?", orderId).Find(&orderLogs).Error
	if err != nil {
		return nil, err
	}
	return orderLogs, nil
}

func (c *UserDao) GetExpressLogByOrderId(orderId uint) ([]*entity.ExpressLog, error) {
	var expressLogs []*entity.ExpressLog
	err := c.db.Where("order_id = ?", orderId).Find(&expressLogs).Error
	if err != nil {
		return nil, err
	}
	return expressLogs, nil
}

func (c *UserDao) GetProductById(productId int) (*entity.Product, error) {
	var product entity.Product
	err := c.db.Where("id = ? AND state = 1", productId).First(&product).Error
	if err != nil {
		return nil, err
	}
	return &product, nil
}

func (c *UserDao) GetProductsByIds(productIds []int) ([]*entity.Product, error) {
	var products []*entity.Product
	err := c.db.Where("id in (?) AND state = 1", productIds).Find(&products).Error
	return products, err
}

func (c *UserDao) GetProductWithImages(productId int) (*entity.Product, error) {
	var product entity.Product
	err := c.db.Preload("ProductImages", func(db *gorm.DB) *gorm.DB {
		return db.Order("sort_order ASC")
	}).Where("id = ? AND state = 1", productId).First(&product).Error
	if err != nil {
		return nil, err
	}
	return &product, nil
}
